# -*- coding: utf-8 -*-
# 姓名:谚语
# 开发时间:2022/10/15 10:45
import os.path

from flask import current_app, request, g
from flask_restful import Resource
from flask_restful.reqparse import RequestParser

from comment.modules import db
from comment.modules.account import Account
from comment.modules.user import User
from comment.utils.Financlal_Redis import fr
from comment.utils.decorators import login_required
from comment.utils.python_sms_sdk_master.example.SendMessage import send_message
from comment.utils.tokens_pyjwt import generate_tokens, ver_tokens
from financial.resources.account.serializer import AccountInfoSerializer
from financial.resources.user import constants
from financial.resources.user.serializer import InvitedListSerializer, UserInfoSerializer


class Login(Resource):
    """
    登录

    RequestParser请求处理
        add_argument()方法的参数：
        第一个参数：验证的参数的名称，
        第二个参数（location）：传递验证数据的方式，
        第三个参数（type）：验证数据的逻辑
    """

    # 视图函数
    def post(self):
        rp = RequestParser()
        rp.add_argument('username', required=True)
        rp.add_argument('password', required=True)
        args = rp.parse_args()
        username = args.username  # 用户
        password = args.password  # 密码

        if not all([username, password]):
            return {'message': '数据不完整', "code": 20001}
        user = User.query.filter(User.username == username).first()
        if user:
            # 验证密码是否正确
            print(user.check_password(password))

            if user.check_password(password):
                # 用户登录成功，之后生成一个token，以便于后面去认证token。  generate_tokens() 生成令牌
                token = generate_tokens(user.id)
                current_app.logger.info(ver_tokens(token))
                # current_app.logger.info('测试验证token', ver_tokens(token))
                return {'token': token}
        return {'message': '用户名或者密码错误。', 'code': 20001}
        # return {'info': '请求成功'}


class LoginOut(Resource):
    """
    退出登录
    """
    # 使用了登录拦截的装饰器[LoginOut整个资源的函数执行前都执行一次装饰器确认是否登录]
    method_decorators = [login_required]

    # 指定哪个函数使用那种装饰器
    # method_decorators = {
    #     'post': [login_required],
    #     'get': [login_required]
    # }

    def post(self):
        if g.user_id:
            g.user_id = None
        return {'msg': '退出成功'}


# 用户头像
# class UserAvatar(Resource):
#     def post(self):
#         # 获取请求里上传的文件
#         imgData = request.files["file"]
#         # 获取当前用户
#         id = g.user_id
#         user = User.query.filter(User.id == id).first()
#
#         # 设置头像存放的路径
#         basedir = os.path.abspath(os.path.join(os.getcwd()))
#         path = basedir + '/static/avatar/'
#         # 设置头像保存的名称和用户id关联
#         imgName = str(id) + '_' + imgData.filename
#
#         # 文件保存的完整地址
#         file_path = path + imgName
#
#         # 保存文件
#         imgData.save(file_path)
#
#         # 保存用户头像名称到数据库
#         if user:
#             user.avatar = imgName
#         db.session.commit()
#
#         return {'msg': 'success', 'avatar': imgName}


class RegisterUser(Resource):
    """
    用户的注册
    """

    def post(self):
        # RequestParser() 获取数据并且验证   required=True 必填
        rp = RequestParser()
        rp.add_argument('phone', required=True)  # 手机号
        rp.add_argument('username', required=True)  # 用户名
        rp.add_argument('password', required=True)  # 密  码
        rp.add_argument('code', required=True)  # 验证码
        rp.add_argument('invite_code')  # 邀请码
        # 验证参数
        args = rp.parse_args()
        phone = args.phone
        username = args.username
        password = args.password
        code = args.code
        invite_code = args.invite_code

        # 验证用户名是否唯一
        u = User.query.filter(User.username == username).first()
        if u:  # 用户名存在
            current_app.logger.info("{}:用户名已存在，请更换用户名".format(username))
            return {'message': '用户名重复', 'code': 20001}

        # 从Redis数据库中获取之前保存的验证码
        real_code = fr.get('registerCode:{}'.format(phone))
        if not real_code or real_code.decode() != code:  # 数据库中没有code或者储存的凑得和参数不一致
            current_app.logger.info('验证码错误或者失效')
            return {'message': '验证码错误或者失效', 'code': 20001}

        # 把用户保存到MySQL数据库中
        u = User(username=username, phone=phone, pwd=password)

        # 验证和关联邀请码
        if invite_code:
            self.check_invite(u, invite_code)

        try:  # 需要用到数据库事务处理
            db.session.add(u)  # 插入数据
            db.session.flush()  # 把数据插入数据库的缓冲区(得到自增ID)，并不是插入到数据库中
            account = Account(userId=u.id)  # 创建当前用户的账户对象
            db.session.add(account)
            db.session.commit()  # 往数据库中插入数据
            return {'msg': 'success'}
        except Exception as ex:
            current_app.logger.error(ex)
            db.session.rollback()  # 回滚[第一步成功第二部不成功就回到第一步]
            return {'message': '用户注册时，数据插入失败', 'code': 20001}

    # 验证和关联邀请码
    def check_invite(self, user, invite_code):
        code = invite_code.strip()  # 去掉首尾空格
        # 老用户的邀请码和注册的用户填入的邀请码做查询比对（及老用戶）
        invite_user = User.query.filter(User.inviteId == code).first()
        if invite_user:
            user.invite_user_id = invite_user.id  # 如果邀请码有效，则把这两个用户关联一下（新用户id=老用户id）
            invite_user.accountInfo.discount += constants.INVITE_MONEY  # 邀请用户的账户中增加50元的代金券
            invite_user.sumFriends = invite_user.sumFriends + 1 if invite_user.sumFriends else 1  # 邀请的记录数 +1


class IsExistPhone(Resource):
    """
    判断手机号是否存在
    """

    def post(self):  # post请求，请求的参数往往会封装成一个json对象
        phone = request.json.get('phone')
        # .query.filterI(查询当前手机号==请求参数中请求的手机号)  过滤查询
        # .first()  查询一条数据
        user = User.query.filter(User.phone == phone).first()
        if user:
            return {"message": '此手机号已注册，请更换手机号码', "code": 20001}
        return {'msg': 'success'}


class SmsCode(Resource):
    """
    发送手机短信，验证手机号
    """

    def get(self):  # get请求，请求的参数往往是: 参数名 + 参数
        # args 拿所有的请求参数  strip 去除空格
        phone = request.args['phone'].strip()
        # 生成一个随机的验证码
        import random
        # randint(范围) 生成一个整数
        code = random.randint(1000, 9999)
        result = send_message(phone, (str(code), '5'))  # 不做短信验证将其注释掉
        # re_dict = json.loads(result)
        # print(re_dict)
        # if result['statusCode'] == '000000':
        #     # current_app日志打印 logger日志输出对象 info日志级别
        #     current_app.logger.info("手机号: {}, 短信发送成功".format(phone))
        #     # 验证码需要存起来，存放到Redis数据库中,还需要拿验证码在注册的请求中去验证。
        #     """
        #     setex 把验证码按照字节数据的格式存到数据库中，有时效性
        #         有三个参数: 1、key  2、时效  3、值
        #     """
        #     fr.setex('registerCode:{}'.format(phone), constants.SMS_CODE_EXPIRES, code)
        #     return {"msg": 'success', 'smsCode': 'code'}
        # else:
        #     return {"message": '此手机号{}，发送短信失败'.format(phone), "code": 20002}
        # 前端显示的看到短信验证码发送成功之后，还需要把验证码存放到redis数据库中，以便于下次验证，验证码的时效为5分钟
        try:
            fr.setex('registerCode:{}'.format(phone), constants.SMS_CODE_EXPIRES, code)  # 参数1：key,参数2：时效
            return {'msg': 'success', 'smsCode': code}
        except Exception as e:
            print(e)
            return {'message': 'redis server error'}


class Invite(Resource):
    """邀请码奖励管理的资源类"""

    # 使用了登录拦截的装饰器[LoginOut整个资源的函数执行前都执行一次装饰器确认是否登录]
    method_decorators = [login_required]

    def post(self):
        """生成邀请码"""
        u_id = g.user_id
        user = User.query.filter(User.id == u_id).first()
        # 根据用户名，采用UUID算法生成一个唯一 邀请码(编号)
        import uuid
        invite_code = str(uuid.uuid5(uuid.NAMESPACE_DNS, user.username))
        user.inviteId = invite_code
        db.session.commit()  # 保存至数据库中
        return {'msg': 'success', 'invite_code': invite_code}

    def get(self):
        """查询被邀请人的列表"""
        u_id = g.user_id

        # 查询被邀请人
        invited_list = User.query.filter(User.invite_user_id == u_id).all()
        if invited_list:
            data = InvitedListSerializer(invited_list).to_dict()
            return {'msg': 'success', 'list': data}
        else:
            return {'msg': 'success', 'list': []}


class UserInfo(Resource):
    """用户信息资源类(包含资金账户)"""
    # 使用了登录拦截的装饰器[LoginOut整个资源的函数执行前都执行一次装饰器确认是否登录]
    method_decorators = [login_required]

    def get(self):
        """当前账户的查询，包括所关联是账户数据"""
        u_id = g.user_id
        user = User.query.filter(User.id == u_id).first()
        # 用户序列化
        user_info_data = UserInfoSerializer(user).to_dict()

        # 查询当前用户所对应的账户信息
        acc = Account.query.filter(Account.userId == u_id).first()

        acc_data = {}
        if acc:
            acc_data = AccountInfoSerializer(acc).to_dict()

        return {
            'role': ['admin'] if user.role else ['user'],  # 用户角色(普通用户/管理员)
            'name': user.username,  # 用户名
            'userInfoData': user_info_data,  # 用户信息数据
            'accountInfo': acc_data  # 用户对应的账户
        }


class UserAvatar(Resource):
    """用户的头像管理"""

    # 使用了登录拦截的装饰器[LoginOut整个资源的函数执行前都执行一次装饰器确认是否登录]
    method_decorators = [login_required]

    def post(self):
        """头像图片上传"""
        u_id = g.user_id
        user = User.query.filter(User.id == u_id).first()
        # 从请求中得到上传的文件数据(得到的是头像文件对象并不只是头像)     request.files[表单提交时的名字] 接收文件上传
        img_data = request.files['file']

        # 指定文件上传的目录，注意: root_path就是当前app所在的目录(绝对路径)
        # join(第一个目录，第二个目录)将两个目录连起来   dirname 拿到上一层目录
        dir_path = os.path.join(current_app.root_path, 'static/avatar')

        # 文件保持到完整路径
        img_name = str(u_id) + '_' + img_data.filename  # 保证头像图片名称不会重复
        file_path = os.path.join(dir_path, img_name)

        try:
            img_data.save(file_path)
        except Exception as e:
            current_app.logger.error(e)
            return {'message': '头像文件上传失败'}

        # 修改数据库
        user.avatar = img_name
        db.session.commit()
        return {'message': 'success', 'avatar': img_name}


class PayPassword(Resource):
    """
    设置支付密码
    """

    def post(self):
        # 定义
        rp = RequestParser()
        rp.add_argument('pwd', required=True)
        # 验证
        args = rp.parse_args()
        pwd = args.pwd
        # 根据用户id找到用户
        id = g.user_id
        user = User.query.filter(User.id == id).first()
        # 判断用户是否存在
        if user:
            user.pay_pwd = pwd
            user.payPwdStatus = 1
            db.session.commit()
            return {'msg': 'success'}
        else:
            return {"code": 20001, 'message': 'server error'}


class LoginPwd(Resource):
    """
    修改登录密码
    """

    def post(self):
        # 定义
        rp = RequestParser()
        rp.add_argument('orgPwd', required=True)
        rp.add_argument('pwd', required=True)
        args = rp.parse_args()
        # 验证
        pwd = args.pwd
        orgPwd = args.orgPwd
        # 根据用户ID查询用户信息
        id = g.user_id
        user = User.query.filter(User.id == id).first()
        # 判断用户是否存在
        if user:
            # 验证密码是否正确
            if user.check_password(orgPwd):
                user.pwd = pwd
                db.session.commit()
                return {'msg': 'success'}
            else:
                return {'code': 20002, 'message': '原密码不正确'}
        return {"code": 20001, 'message': 'server error'}


class RealNameAuth(Resource):
    """
    实名认证
    """

    def post(self):
        # 定义
        rp = RequestParser()
        rp.add_argument('realName', required=True)
        rp.add_argument('idNum', required=True)
        # 验证
        args = rp.parse_args()
        realName = args.realName
        idNum = args.idNum
        # 根据用户id查找用户信息
        id = g.user_id
        user = User.query.filter(User.id == id).first()
        status = user.realName
        print(status)
        # 判断用户真实姓名
        if user.realName:
            return {"code": 20001, 'message': '不可重复验证'}
        else:
            user.realName = realName  # 名字
            user.realNameStatus = 1  # 实名状态
            user.idNum = idNum  # 身份证
            db.session.commit()
            return {'msg': 'success'}
